package com.zmh.wt.healthmonitor.service.impl;

import com.github.benmanes.caffeine.cache.Cache;
import com.zmh.wt.healthmonitor.bo.FeaCurveBO;
import com.zmh.wt.healthmonitor.common.Result;
import com.zmh.wt.healthmonitor.constant.CacheConstant;
import com.zmh.wt.healthmonitor.constant.Constants;
import com.zmh.wt.healthmonitor.entity.FeaPointDO;
import com.zmh.wt.healthmonitor.entity.RealtimeDO;
import com.zmh.wt.healthmonitor.entity.SpectrumDo;
import com.zmh.wt.healthmonitor.entity.TxtFileDo;
import com.zmh.wt.healthmonitor.mapper.RealtimeMapper;
import com.zmh.wt.healthmonitor.service.RealTimeService;
import com.zmh.wt.healthmonitor.service.WindturbineService;
import com.zmh.wt.healthmonitor.util.CommonUtil;
import com.zmh.wt.healthmonitor.utils.FFTUtil;
import com.zmh.wt.healthmonitor.utils.Txt2WavUtil;
import com.zmh.wt.healthmonitor.utils.TxtUtil;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author MH.Zhang
 * @Classname RealtimeServiceImpl
 * @Date 2023/2/20 17:09
 */
@Service
@Slf4j
public class RealtimeServiceImpl implements RealTimeService {

    private final static String MAX_WT_ID = "max_wt_id";
    private final static String FEA_CURVE = "fea_curve";
    private final static String LATEST_FILE = "latest_file";
    private final static String WT_STATUS = "wt_status";

    @Resource
    RealtimeMapper realtimeMapper;

    @Resource
    WindturbineService windturbineService;

    @Qualifier("commonCache")
    @Autowired
    Cache<String, Object> caffeineCache;

    @Qualifier("stateCache")
    @Autowired
    Cache<String, Object> stateCache;

    @Autowired
    Environment environment;

    @Value("${realtime.dataLength}")
    Integer dataLength;

    @Value("${realtime.downsample.ratio}")
    Integer downsampleRatio;

    @Value("${realtime.downsample.open}")
    Boolean downsampleOpenFlag;

    @Value("${realtime.spectrum.segment.num}")
    Integer segmentNum;

    @Value("${realtime.spectrum.downsample.open}")
    Boolean specDownsampleOpenFlag;

    @Value("${realtime.spectrum.downsample.ratio}")
    Integer specDownsampleRatio;

    @Value("${realtime.spectrum.out.downsample.ratio}")
    Integer specOutDownsampleRatio;

    @Value("${realtime.log.open}")
    Boolean logOpen;


    /**
     * 更新实时数据
     *
     * @param realtimeDO
     * @return
     */
    @Override
    public Result insertRealtimeData(RealtimeDO realtimeDO) {

        //数据判空
        if (null == realtimeDO || Objects.isNull(realtimeDO.getStatus())) {
            log.error("实时数据为空,详细信息为realtimeDO={}", realtimeDO);
            return Result.buildResult(Result.Status.ERROR);
        }

        String windfarm = realtimeDO.getWindfarm();
        Integer windturbine = realtimeDO.getWindturbine();
        String windturbineStr = windturbine.toString();
        //编号校验
        if (Objects.isNull(windturbine) || Objects.isNull(windfarm)) {
            log.error("风场-{}存在风机编号为空实时数据,详细信息为{}", windfarm, realtimeDO);
            return Result.buildResult(Result.Status.ERROR, "风机编号为空");
        }

        //已连接状态特殊处理
        Double connectedFlag = realtimeDO.getFeature1() + realtimeDO.getFeature2() + realtimeDO.getFeature3();
        Integer status = realtimeDO.getStatus();
        String wtStatusKey = CacheConstant.getWtStatusKey(windfarm, windturbineStr);

//        if (connectedFlag < 0.0001) {
//            stateCache.getIfPresent(wtStatusKey);
//            Integer wtStatus = (Integer) stateCache.asMap().get(wtStatusKey);
//            //更新
//            if (wtStatus == null || wtStatus.equals(Constants.UNCONNECTED)
//                    || wtStatus.equals(Constants.CONNECTED)) {
//                stateCache.put(wtStatusKey, Constants.CONNECTED);
//            }
////            String wfStatusKey = CacheConstant.getKey(CacheConstant.KEY_WIND_TURBINE, WT_STATUS, windfarm);
////            Map<String, Integer> wfStatus = (Map<String, Integer>) stateCache.asMap().get(wtStatusKey);
//
//            return Result.buildResult(Result.Status.SUCCESS);
//        }

        //最大风机编号缓存处理
        String maxWindturbineIdKey = CacheConstant.getKey(CacheConstant.KEY_REAL_TIME, MAX_WT_ID, windfarm);
        caffeineCache.getIfPresent(maxWindturbineIdKey);
        Integer maxId = (Integer) caffeineCache.asMap().get(maxWindturbineIdKey);

        //最大风机编号缓存非空且需更新
        if (null != maxId && windturbine.compareTo(maxId) > Constants.ZERO) {
            Integer updateId = Math.max(maxId, windturbine);
            caffeineCache.put(maxWindturbineIdKey, updateId);
            log.info("风场-{}的最大风机编号缓存更新为: {}", windfarm, updateId);
        }

        //数据库更新
        realtimeMapper.insert(realtimeDO);

        //特征曲线更新
        updateFeaCurve(realtimeDO);

        //风机状态更新
        stateCache.put(wtStatusKey, status);
//        windturbineService.updateWindturbineCacheStatus(windfarm, windturbine, realtimeDO.getStatus());

        return Result.buildResult(Result.Status.SUCCESS);
    }

    /**
     * 更新当前风场的特征曲线数据
     *
     * @param realtimeDO
     * @return
     */
    @Override
    public Integer updateFeaCurve(RealtimeDO realtimeDO) {

        String windfarm = realtimeDO.getWindfarm();
        Integer windturbine = realtimeDO.getWindturbine();

        //异常处理
        if (windfarm == null || windturbine == null) {
            return Constants.FAIL_INT;
        }

        //查缓存
        String wtSuffix = windfarm.concat(String.valueOf(windturbine));
        String feaCurveKey = CacheConstant.getKey(CacheConstant.KEY_REAL_TIME, FEA_CURVE, wtSuffix);
        caffeineCache.getIfPresent(feaCurveKey);
        FeaCurveBO feaCurve = (FeaCurveBO) caffeineCache.asMap().get(feaCurveKey);

        //若缓存为空,则构建缓存value
        if (feaCurve == null) {
            Integer cap = Integer.parseInt(environment.getProperty("realtime.feacurve.capacity"));
            feaCurve = FeaCurveBO.builder().
                    capacity(cap).
                    windfarm(windfarm).
                    windturbine(realtimeDO.getWindturbine()).
                    feePoints(new LinkedList<>()).
                    build();
        }

        //feaCurve特征点注入
        FeaPointDO feaPointDO = FeaPointDO.builder().
                feature1(realtimeDO.getFeature1()).
                feature2(realtimeDO.getFeature2()).
                feature3(realtimeDO.getFeature3()).
                gmtReceived(realtimeDO.getGmtReceived()).build();
        Integer addRes = feaCurve.addFeePoint(feaPointDO);

        if (addRes.equals(Constants.FAIL_INT)) {
            return Constants.FAIL_INT;
        }

        return Constants.SUCCESS_INT;
    }

    /**
     * 获取风机的最新实时数据
     *
     * @param windfarm
     * @param windturbine
     * @return
     */
    @Override
    public Result getFeaCurve(String windfarm, Integer windturbine) {

        //异常处理
        if (windfarm == null || windturbine == null) {
            return Result.buildResult(Result.Status.ERROR);
        }

        //查缓存
        String wtSuffix = windfarm.concat(String.valueOf(windturbine));
        String feaCurveKey = CacheConstant.getKey(CacheConstant.KEY_REAL_TIME, FEA_CURVE, wtSuffix);
        FeaCurveBO feaCurve = (FeaCurveBO) caffeineCache.getIfPresent(feaCurveKey);

        if (feaCurve == null) {
            //获取配置中的特征曲线长度
            Integer cap = Integer.parseInt(environment.getProperty("realtime.feacurve.capacity"));
            LinkedList<FeaPointDO> feaPoints = realtimeMapper.queryLastNRecord(windfarm, windturbine, cap);

            if (Constants.ZERO.equals(feaPoints.size())) {
                return Result.buildResult(Result.Status.NOT_FOUND);
            }

            //构造特征曲线
            feaCurve = FeaCurveBO.builder().
                    capacity(cap).
                    windfarm(windfarm).
                    windturbine(windturbine).
                    feePoints(feaPoints).
                    build();

            //更新缓存
            caffeineCache.put(feaCurveKey, feaCurve);

            return Result.buildResult(Result.Status.SUCCESS, feaCurve);
        }

        return Result.buildResult(Result.Status.SUCCESS, feaCurve);
    }

    /**
     * 当前风场最大风机编号
     *
     * @param windfarm
     * @return
     */
    @Override
    public Integer getMaxWindturbineId(String windfarm) {
        //组合key
        String maxWindturbineIdKey = CacheConstant.getKey(CacheConstant.KEY_REAL_TIME, MAX_WT_ID, windfarm);
        caffeineCache.getIfPresent(maxWindturbineIdKey);
        Integer maxId = (Integer) caffeineCache.asMap().get(maxWindturbineIdKey);

        if (null == maxId) {
            //更新缓存
            maxId = realtimeMapper.searchMaxWindturbineId(windfarm);
            caffeineCache.put(maxWindturbineIdKey, maxId);
            log.info("风场-{}的最大风机编号缓存更新为: {}", windfarm, maxId);
        }

        return maxId;
    }

    /**
     * 当前监测风机数量
     *
     * @param windfarm
     * @return
     */
    @Override
    public Integer getWindturbineNum(String windfarm) {
        return null;
    }

    /**
     * 获得最新文件绝对路径
     *
     * @return
     */
    private Pair<String, String> getLatestFileAbsolutePath() {
        String latestFileKey = CacheConstant.getKey(CacheConstant.KEY_REAL_TIME, LATEST_FILE);
//        caffeineCache.getIfPresent(latestFileKey);
        Pair<String, String> fileNameAndPathPair = (Pair<String, String>) caffeineCache.asMap().get(latestFileKey);

        return fileNameAndPathPair;
    }

    /**
     * 获取最新wav文件绝对路径（滤波）
     *
     * @return
     */
    @Override
    public Result getLatestWavAbsolutePath() throws IOException {

        Pair<String, String> fileNameAndPathPair = getLatestFileAbsolutePath();

        //pair校验
        if (fileNameAndPathPair == null) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        String latestFileAbsolutePath = fileNameAndPathPair.getRight();
        //路径校验
        if (latestFileAbsolutePath == null || latestFileAbsolutePath.length() == Constants.ZERO) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(latestFileAbsolutePath);
        Double[] txtData = txtFileDo.getData();

        //数据校验
        if (txtData == null || txtData.length == Constants.ZERO) {
            return Result.buildResult(Result.Status.INTERNAL_SERVER_ERROR, "服务器数据处理异常");
        }

        TxtFileDo highpassDo = TxtUtil.highpass(txtData);

        //文件名校验
        String fullWavName = fileNameAndPathPair.getLeft();
        if (fullWavName == null || fullWavName.length() == 0) {
            return Result.buildResult(Result.Status.INTERNAL_SERVER_ERROR, "服务器数据处理异常");
        }
        String wavName = fullWavName.substring(0, fullWavName.length() - 4);
        Integer resFlag = Txt2WavUtil.doubleDataToWavFile(highpassDo.getData(), wavName, highpassDo.getMax(), highpassDo.getMin());

        //文件转换校验
        if (Constants.FAIL_INT.equals(resFlag)) {
            return Result.buildResult(Result.Status.INTERNAL_SERVER_ERROR);
        }

        //路径处理
        String projectAbsolutePath = System.getProperty("user.dir");
        StringBuilder wavFileAbsolutePathAddr = new StringBuilder();
        String wavFileAbsolutePath = wavFileAbsolutePathAddr.append(projectAbsolutePath)
                .append("\\wavFile\\")
                .append(wavName)
                .append(".wav")
                .toString()
                .replace("\\", "/");

        return Result.buildResult(Result.Status.SUCCESS, "ok", wavFileAbsolutePath);
    }

    /**
     * 获取最新txt文件的绝对路径（滤波）
     *
     * @return
     * @throws IOException
     */
    @Override
    public Result getLatestTxtAbsolutePath() throws IOException {
        Pair<String, String> fileNameAndPathPair = getLatestFileAbsolutePath();

        //pair校验
        if (fileNameAndPathPair == null) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        String latestFileAbsolutePath = fileNameAndPathPair.getRight();
        //路径校验
        if (latestFileAbsolutePath == null || latestFileAbsolutePath.length() == Constants.ZERO) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(latestFileAbsolutePath);
        Double[] txtData = txtFileDo.getData();

        //数据校验
        if (txtData == null || txtData.length == Constants.ZERO) {
            return Result.buildResult(Result.Status.INTERNAL_SERVER_ERROR, "服务器数据处理异常");
        }

        TxtFileDo highpassDo = TxtUtil.highpass(txtData);
        Double[] highpassData = highpassDo.getData();

        String orgFileName = fileNameAndPathPair.getLeft();
        String projectAbsolutePath = System.getProperty("user.dir");
        StringBuilder txtFilePathBuilder = new StringBuilder();
        String txtAbsoluteFilePath = txtFilePathBuilder
                .append(projectAbsolutePath)
                .append("\\wavFile\\")
                .append(orgFileName, 0, orgFileName.length() - 4)
                .append("_after_highpass.txt")
                .toString()
                .replace("\\", "/");

        TxtUtil.writeTxtFile(txtAbsoluteFilePath, highpassData);

        return Result.buildResult(Result.Status.SUCCESS, "ok", txtAbsoluteFilePath);
    }

    /**
     * 获取最新txt文件的绝对路径（原始数据，无滤波）
     *
     * @return
     */
    @Override
    public Result getLatestOrigTxtAbsolutePath() {
        Pair<String, String> fileNameAndPathPair = getLatestFileAbsolutePath();

        //pair校验
        if (fileNameAndPathPair == null) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        String latestFileAbsolutePath = fileNameAndPathPair.getRight();
        //路径校验
        if (latestFileAbsolutePath == null || latestFileAbsolutePath.length() == Constants.ZERO) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        String formatPath = latestFileAbsolutePath.replace("\\", "/");
        return Result.buildResult(Result.Status.SUCCESS, "ok", formatPath);
    }

    /**
     * 获取最新文件的频谱数据（滤波）
     *
     * @return
     */
    @Override
    public Result getLastFileSpectrum() {

        Pair<String, String> fileNameAndPathPair = getLatestFileAbsolutePath();

        //pair校验
        if (fileNameAndPathPair == null) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        String latestFileAbsolutePath = fileNameAndPathPair.getRight();
        //路径校验
        if (latestFileAbsolutePath == null || latestFileAbsolutePath.length() == Constants.ZERO) {
            return Result.buildResult(Result.Status.NOT_FOUND, "数据为空");
        }

        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(latestFileAbsolutePath);
        double fs = 44100.0;
        Double[] orgData = txtFileDo.getData();
        Double[] data = TxtUtil.highpass(orgData).getData();

        int N = 2;
        while (N < data.length) {
            N *= 2;
        }

        double[] doubles = Stream.of(data).mapToDouble(Double::doubleValue).toArray();
        SpectrumDo spectrumDo = FFTUtil.caculateSpectrum(doubles, fs, true, N);

        return Result.buildResult(Result.Status.SUCCESS, "ok", spectrumDo);
    }

    /**
     * 查询风场最新N条风机实时数据记录
     *
     * @return
     */
    @Override
    public Result queryWindFarmLastRecord(String windfarm, Integer N) {
        List<RealtimeDO> list = realtimeMapper.queryWindFarmLastRecord(windfarm, N);
        return Result.buildResult(Result.Status.SUCCESS, "ok", list);
    }

    /**
     * 查询风场某状态下最新N条风机实时数据记录
     *
     * @return
     */
    @Override
    public Result queryWindFarmLastRecordByStatus(String windfarm, Integer status, Integer N) {
        List<RealtimeDO> list = realtimeMapper.queryWindFarmLastRecordByStatus(windfarm, status, N);
        return Result.buildResult(Result.Status.SUCCESS, "ok", list);
    }

    /**
     * 获取最新滤波前txt文件20万点（入参：风机id）
     * 加入降采样 2023/4/26
     *
     * @param windturbine
     * @return
     */
    @Override
    public Double[] getLatestOrigTxtData(String windturbine) {
        Pair<String, String> latestFileAbsolutePath = getLatestFileAbsolutePath(windturbine);
        if (Objects.isNull(latestFileAbsolutePath)) {
            return new Double[0];
        }
        String absolutePath = latestFileAbsolutePath.getRight();
        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(absolutePath);
        Double[] origData = Arrays.copyOfRange(txtFileDo.getData(), 0, dataLength);
        Double[] res = origData;
        if (downsampleOpenFlag) {
            res = CommonUtil.downsample(origData, downsampleRatio);
        }

//        if (logOpen) {
//            res = Arrays.stream(res).map(org -> 20 * Math.log(Math.abs(org / 10.0) + 10e-2) * (org >= 0 ? 1 : -1)).toArray(Double[]::new);
//        }

        return res;
    }

    /**
     * 获取最新滤波后txt文件20万点 11
     *
     * @param windturbine
     * @return
     */
    @Override
    public Double[] getLatestTxtDataAfterFiltering(String windturbine) {

        Pair<String, String> latestFileAbsolutePath = getLatestFileAbsolutePath(windturbine);
        if (Objects.isNull(latestFileAbsolutePath)) {
            return new Double[0];
        }
        String absolutePath = latestFileAbsolutePath.getRight();
        //TODO 文件地址校验
        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(absolutePath);
        Double[] origData = Arrays.copyOfRange(txtFileDo.getData(), 0, dataLength);
        TxtFileDo highpass = TxtUtil.highpass(origData);

        Double[] resData = highpass.getData();

        //TODO 降采样及log复用
        if (downsampleOpenFlag) {
            resData = CommonUtil.downsample(highpass.getData(), downsampleRatio);
        }

//        if (logOpen) {
//            resData = Arrays.stream(resData).map(org -> 20 * Math.log(Math.abs(org / 10.0) + 10e-2) * (org >= 0 ? 1 : -1)).toArray(Double[]::new);
//        }

        return resData;
    }

    /**
     * 频谱txt文件20万点
     *
     * @param windturbine
     * @return
     */
    @Override
    public SpectrumDo getLatestTxtSpectrumData(String windturbine) {
        Pair<String, String> latestFileAbsolutePath = getLatestFileAbsolutePath(windturbine);
        if (Objects.isNull(latestFileAbsolutePath)) {
            return null;
        }
        String absolutePath = latestFileAbsolutePath.getRight();
        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(absolutePath);
        Double[] origData = Arrays.copyOfRange(txtFileDo.getData(), 0, dataLength);
        TxtFileDo highpass = TxtUtil.highpass(origData);
        Double[] data = highpass.getData();
        double[] doubles = Stream.of(data).mapToDouble(Double::doubleValue).toArray();
        int N = 2;
        while (N < doubles.length) {
            N *= 2;
        }
        SpectrumDo spectrumDo = FFTUtil.caculateSpectrum(doubles, 44100.0, true, N);
        return spectrumDo;
    }

    /**
     * 分段频谱20组 22
     *
     * @param windturbine
     * @return
     */
    @Override
    public List<double[]> getLatestTxtSpectrumCollection(String windturbine) {
        Pair<String, String> latestFileAbsolutePath = getLatestFileAbsolutePath(windturbine);
        if (Objects.isNull(latestFileAbsolutePath)) {
            return null;
        }
        String absolutePath = latestFileAbsolutePath.getRight();
        TxtFileDo txtFileDo = TxtUtil.readTxtByPath(absolutePath);
        Double[] origData = Arrays.copyOfRange(txtFileDo.getData(), 0, dataLength);
        TxtFileDo highpass = TxtUtil.highpass(origData);
        Double[] data = highpass.getData();

        double[] initData;
        if (specDownsampleOpenFlag) {
            data = CommonUtil.downsample(data, specDownsampleRatio);
        }

        if (logOpen) {
            data = Arrays.stream(data).map(org -> 20 * Math.log(Math.abs(org / 10.0) + 10e-2) * (org >= 0 ? 1 : -1)).toArray(Double[]::new);
        }

        initData = Stream.of(data).mapToDouble(Double::doubleValue).toArray();

        //拆分
        List<double[]> res = new ArrayList<>();
        int segLen = initData.length / segmentNum;
        int N = 2;
        while (N < segLen) {
            N *= 2;
        }

        Double Fs = 44100.0 / downsampleRatio;
        for (int i = 0; i < initData.length; i = i + segLen) {
            double[] cur = Arrays.copyOfRange(initData, i, i + segLen);
            SpectrumDo spectrumDo = FFTUtil.caculateSpectrum(cur, Fs, true, N);
            double[] amplitude = spectrumDo.getAmplitude();
            if (specOutDownsampleRatio != null && specOutDownsampleRatio > 1) {
                amplitude = CommonUtil.downsample(spectrumDo.getAmplitude(), specOutDownsampleRatio);
            }
            res.add(amplitude);
        }
//        SpectrumDo spectrumDo = FFTUtil.caculateSpectrum(initData, 44100.0, true, N);
        return res;
    }

    /**
     * @param windturbine
     * @return
     */
    private Pair<String, String> getLatestFileAbsolutePath(String windturbine) {
        String latestFileKey = CacheConstant.getLatestFileKeyById(windturbine);
        caffeineCache.getIfPresent(latestFileKey);
        Pair<String, String> fileNameAndPathPair = (Pair<String, String>) caffeineCache.asMap().get(latestFileKey);

        return fileNameAndPathPair;
    }
}
